package com.roadmap.util.chart;

import java.util.Calendar;
import java.util.Date;

import org.apache.log4j.Logger;

import com.roadmap.common.BaseConst;
import com.roadmap.common.RdcLogger;
import com.roadmap.common.BaseEnum.TimeType;
import com.roadmap.exception.NullObjectException;

public class DateTimeUtility {
	private final static Logger logger = RdcLogger
			.getLogger(DateTimeUtility.class);

	/**
	 * compute the time diff between two dates
	 * 
	 * @param type:
	 *            if TimeType.DAY chosen, return the days between the two dates;
	 *            if TimeType.HOUR chosen, return the hours between two dates
	 */
	public static int getTimeDiffBetween(Date big, Date small, TimeType type)
			throws NullObjectException {
		if (big == null || small == null) {
			NullObjectException noe = new NullObjectException(
					"the entity can't be NULL");
			logger.error(noe);
			throw noe;
		}
		long slice = 0;
		if (type == TimeType.DAY)
			slice = BaseConst.DayToMillion;
		else if (type == TimeType.HOUR)
			slice = BaseConst.HourToMillion;
		long diff = (big.getTime() - small.getTime()) / slice;
		return (int) diff;
	}

	/** get the date before current day and the date after it */
	public static Date[] getFirstAndLastDayOfThreeDays(Date date)
			throws NullObjectException {
		if (date == null) {
			NullObjectException noe = new NullObjectException(
					"the date can't be NULL");
			logger.error(noe);
			throw noe;
		}

		Date[] dates = new Date[2];
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		long curr = date.getTime();
		long fst = curr - BaseConst.DayToMillion;
		long lst = curr + BaseConst.DayToMillion;

		cal.setTimeInMillis(fst);
		dates[0] = cal.getTime();
		cal.setTimeInMillis(lst);
		dates[1] = cal.getTime();
		return dates;
	}

	/** get the first date and last date of a week specified by a specific day */
	public static Date[] getFirstAndLastDayOfWeek(Date date)
			throws NullObjectException {
		if (date == null) {
			NullObjectException noe = new NullObjectException(
					"the date can't be NULL");
			logger.error(noe);
			throw noe;
		}

		Date[] dates = new Date[2];

		// to identify which day of week the date is
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		// dayInWeek: 0~6 -> Sunday~Saturday
		int dayInWeek = cal.get(Calendar.DAY_OF_WEEK) - 1;
		long curr = date.getTime();
		// the first day of this week
		long fst = curr - BaseConst.DayToMillion * dayInWeek;
		// the last day of this week
		long lst = curr + BaseConst.DayToMillion
				* (BaseConst.DaysOfWeek - dayInWeek - 1);
		cal.setTimeInMillis(fst);
		dates[0] = cal.getTime();
		cal.setTimeInMillis(lst);
		dates[1] = cal.getTime();

		return dates;
	}

	/**
	 * get the first date and last date of a month which the current day belongs
	 * to
	 */
	public static Date[] getFirstAndLastDayOfMonth(Date date)
			throws NullObjectException {
		if (date == null) {
			NullObjectException noe = new NullObjectException(
					"the date can't be NULL");
			logger.error(noe);
			throw noe;
		}

		Date[] dates = new Date[2];
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		// how many days of this month
		int daysOfMonth = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
		// which day the given date is in this month
		int dayInMonth = cal.get(Calendar.DAY_OF_MONTH) - 1;
		long curr = date.getTime();
		// the first day of this month
		long fst = curr - BaseConst.DayToMillion * dayInMonth;
		// the last day of this month
		long lst = curr + BaseConst.DayToMillion
				* (daysOfMonth - dayInMonth - 1);

		cal.setTimeInMillis(fst);
		dates[0] = cal.getTime();
		cal.setTimeInMillis(lst);
		dates[1] = cal.getTime();

		return dates;
	}

	/**
	 * find out the dates between a date scope [first, last], and the dates are
	 * generated by repeating times from start. first <= (start + cycle * x) <=
	 * min {end, last}. resolve this inequality and get the value x.
	 */
	public static Date[] getDatesBetween(Date first, Date last, Date start,
			Date end, int cycle) throws NullObjectException {
		if (first == null || last == null || start == null || end == null
				|| cycle < 0) {
			NullObjectException noe = new NullObjectException(
					"the date can't be NULL");
			logger.error(noe);
			throw noe;
		}
		long lgFirst = first.getTime();
		long lgLast = last.getTime();
		long lgStart = start.getTime();
		long lgEnd = end.getTime();

		if (lgStart > lgLast || lgEnd < lgFirst) {
			logger
					.error("the given date is not between first date and last date.");
			return null;
		}

		long interval = cycle * BaseConst.DayToMillion;
		long shang = 0;
		long dfs = 0;
		if (lgFirst > lgStart) {
			// 如果first大于start，那么start需要增加几次interval
			dfs = (lgFirst - lgStart) / interval;
			shang = (lgFirst - lgStart) % interval;
			if (shang != 0)
				dfs++;
		}
		long dfe = 0;
		if (lgEnd <= lgLast) {
			dfe = (lgEnd - lgStart) / interval;
		}
		else {
			dfe = (lgLast - lgStart) / interval;
		}

		int count = (int) (dfe - dfs + 1);
		if (count <= 0)
			return null;
		Date[] dates = new Date[count];
		count = 0;
		Calendar cal = Calendar.getInstance();
		for (long x = dfs; x <= dfe; x++) {
			long ldt = lgStart + interval * x;
			cal.setTimeInMillis(ldt);
			dates[count++] = cal.getTime();
		}

		return dates;
	}

}
